package cn.shujuhai.cms.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.shujuhai.cms.domain.EmailLog;
import cn.shujuhai.cms.domain.EmailRecord;
import cn.shujuhai.cms.domain.InfoContent;
import cn.shujuhai.cms.domain.Recipient;
import cn.shujuhai.cms.mapper.EmailRecordMapper;
import cn.shujuhai.cms.service.EmailLogService;
import cn.shujuhai.cms.service.EmailRecordService;
import cn.shujuhai.cms.service.InfoContentService;
import cn.shujuhai.cms.service.RecipientService;
import cn.shujuhai.common.pluging.data.ResultCode;
import cn.shujuhai.common.pluging.data.ResultVo;
import cn.shujuhai.common.pluging.file.FilesUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @author niuyahu
 * @description 针对表【email_record(邮件记录表，用于存储邮件的基本信息、状态及内容)】的数据库操作Service实现
 * @createDate 2024-12-24 14:59:02
 */
@Service
@RequiredArgsConstructor
public class EmailRecordServiceImpl extends ServiceImpl<EmailRecordMapper, EmailRecord>
        implements EmailRecordService {

    @Value("${template.static.emailAttachmentUrl}")
    public String UPLOAD_EMAIL_ATTACHMENT_DIR; //邮件附件路径


    @Value("${template.static.viewEmailAttachmentUrl}")
    public String VIEW_EMAIL_ATTACHMENT_DIR; //邮件附件访问路径
    private final EmailLogService emailLogService;
    private final InfoContentService infoContentService;
    private final RecipientService recipientService;
    private final SendEmailServiceImpl sendEmailService;

    /**
     * 分页查询系统邮件信息
     *
     * @param subject  邮箱主题（可为null，不为null时进行模糊查询）
     * @param page     当前页码（从1开始）
     * @param pageSize 每页显示的记录数
     * @return 返回包含分页信息和系统邮件列表的ResultVo对象，其中PageInfo<EmailRecord>包含了分页的详细信息，如总记录数、总页数等
     */
    @Override
    public ResultVo<PageInfo<EmailRecord>> getEmailPageInfo(String subject, Integer page, Integer pageSize) {
        PageHelper.startPage(page, pageSize);
        var wrapper = new MPJLambdaWrapper<EmailRecord>()
                .selectAll(EmailRecord.class)
                .leftJoin(EmailLog.class, EmailLog::getEmailId, EmailRecord::getId)
                .groupBy(EmailRecord::getId)
                .select("(SELECT COUNT(ID) FROM email_log WHERE id IN " +
                        "(SELECT MAX(id) FROM email_log t2 WHERE t2.email_id = t1.email_id GROUP BY recipient_id) AND STATUS = 0) AS fail_num")
                .select("(SELECT COUNT(ID) FROM email_log WHERE id IN " +
                        "(SELECT MAX(id) FROM email_log t2 WHERE t2.email_id = t1.email_id GROUP BY recipient_id) AND STATUS = 1) AS success_num")
                .select("(SELECT COUNT(ID) FROM group_recipient WHERE group_id = t.group_id AND deleted = 0) AS recipient_num")
                .orderByDesc(EmailRecord::getId);
        if (subject != null && !subject.isEmpty()) {
            wrapper.like("subject", subject);
        }
        List<EmailRecord> EmailList = this.list(wrapper);  // 根据用户Id降序排序
        PageInfo<EmailRecord> EmailPageInfo = new PageInfo<>(EmailList);
        return ResultVo.success(EmailPageInfo);
    }

    /**
     * 新增邮箱对象
     *
     * @param emailRecord 邮箱对象
     * @return 新增成功或失败的响应状态
     */
    @Override
    public ResultVo<?> add(EmailRecord emailRecord) {
//        byte[] content = this.infoContentService.getOne(Wrappers.<InfoContent>lambdaQuery().eq(InfoContent::getArticleId, emailRecord.getArticleId())).getContent();
//        emailRecord.setContent(content);
        if (this.save(emailRecord)) {
            return ResultVo.success();
        }
        return ResultVo.failure(ResultCode.ERROR);
    }

    /**
     * 邮件编辑
     *
     * @param emailRecord 邮件对象
     * @return 更新的邮箱对象
     */
    @Override
    public ResultVo<EmailRecord> edit(EmailRecord emailRecord) {
//        byte[] content = this.infoContentService.getOne(Wrappers.<InfoContent>lambdaQuery().eq(InfoContent::getArticleId, emailRecord.getArticleId())).getContent();
//        emailRecord.setContent(content);
        if (this.updateById(emailRecord)) {
            return ResultVo.success();
        }
        return ResultVo.failure(ResultCode.ERROR);
    }

    /**
     * 邮件删除
     *
     * @param id 邮件id
     * @return 删除成功或失败的结果及响应信息
     */
    @Override
    public ResultVo<EmailRecord> removeId(Integer id) {
        if (!this.removeById(id)) {
            return ResultVo.failure(ResultCode.FAILED);
        }
        return ResultVo.success();
    }

    /**
     * 邮件附件上传
     *
     * @param file 文件内容
     * @return 结果信息
     */
    @Override
    public ResultVo<?> uploadAttachment(MultipartFile file) {
        try {
            // 校验文件类型，支持的压缩包格式
            String fileName = file.getOriginalFilename();
            if (fileName == null || !fileName.matches("^(?i).*(\\.zip|\\.rar|\\.7z|\\.tar|\\.gz)$")) {
                return ResultVo.failure("仅支持上传压缩包文件 (.zip, .rar, .7z, .tar, .gz)");
            }
            // 校验文件大小
            long maxSize = 200 * 1024 * 1024;
            if (file.getSize() > maxSize) {
                return ResultVo.failure("文件大小不能超过 200MB");
            }
            String uploadedFile = FilesUtils.uploadFile(file, UPLOAD_EMAIL_ATTACHMENT_DIR, VIEW_EMAIL_ATTACHMENT_DIR);
            return ResultVo.success(uploadedFile);
        } catch (RuntimeException e) {
            return ResultVo.failure("文件上传失败" + e);
        }
    }

    /**
     * 发送邮件的方法，根据提供的邮件记录ID执行邮件发送操作，并记录发送结果。
     *
     * @param id 邮件记录的ID
     * @return 邮件发送的结果封装在ResultVo对象中
     */
    @Override
    public ResultVo<?> sending(Long id, String sendTime) {
        EmailRecord emailRecord = this.getById(id);
        var wrapper = this.recipientService.getRecipientListByGroupId(emailRecord.getGroupId());
        List<Recipient> recipientList = this.recipientService.list(wrapper);
        LocalDateTime sendDateTime = null;
        if (StrUtil.isNotEmpty(sendTime)) {
            try {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                sendDateTime = LocalDateTime.parse(sendTime, formatter);
            } catch (DateTimeParseException e) {
                return ResultVo.failure(ResultCode.ERROR);
            }
        }
        int state = sendDateTime != null && sendDateTime.isBefore(LocalDateTime.now()) ? 2 : 1;
        boolean updatedById = this.update(Wrappers.<EmailRecord>lambdaUpdate()
                .set(EmailRecord::getStatus, state)
                .set(EmailRecord::getSendTime, sendDateTime)
                .eq(EmailRecord::getId, emailRecord.getId()));
        if (updatedById) {
            if (state == 2) {
                sendEmailService.sendEmail(emailRecord, recipientList);
            }
            return ResultVo.success();
        }
        return ResultVo.failure(ResultCode.ERROR);
    }

    /**
     * 根据邮件ID获取日志记录
     *
     * @param page     页码
     * @param pageSize 每页大小
     * @param emailId  邮件ID
     * @param status   邮件发送状态
     * @param email    查询条件（邮箱）
     * @return 获取的日志记录
     */
    @Override
    public ResultVo<?> getEmailLogByEmailId(Integer page, Integer pageSize, Long emailId, Integer status, String email) {
        PageHelper.startPage(page, pageSize);
        var wrapper = new MPJLambdaWrapper<EmailLog>()
                .selectAll(EmailLog.class)
                .select(Recipient::getEmail)
                .select(Recipient::getName)
                .leftJoin(EmailRecord.class, EmailRecord::getId, EmailLog::getEmailId)
                .leftJoin(Recipient.class, Recipient::getId, EmailLog::getRecipientId)
                .eq(EmailLog::getEmailId, emailId)
                .eq(EmailLog::getStatus, status)
                .like(Recipient::getEmail, email)
                .orderByDesc(EmailLog::getId);
        List<EmailLog> emailLogList = this.emailLogService.list(wrapper);
        PageInfo<EmailLog> EmailLogPageInfo = new PageInfo<>(emailLogList);
        return ResultVo.success(EmailLogPageInfo);
    }

    /**
     * 定时发送邮件的任务
     */
    @Async("getAsyncExecutor")
    @Override
    public void doSendEmailSchedule() {
        List<EmailRecord> list = this.list(Wrappers.<EmailRecord>lambdaQuery()
                .eq(EmailRecord::getStatus, 1)
                .le(EmailRecord::getSendTime, LocalDateTime.now()));
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        for (EmailRecord emailRecord : list) {
            sending(emailRecord.getId(), emailRecord.getSendTime().format(formatter));
        }
    }

    /**
     * 重新发送邮件的方法，根据提供的邮件记录ID执行邮件重新发送操作，并记录发送结果。
     *
     * @param id 邮件记录的ID
     * @return 邮件发送的结果封装在ResultVo对象中
     */
    @Override
    public ResultVo<?> resend(Long id, String sendTime) {
        EmailRecord emailRecord = this.getById(id);
        if (emailRecord.getRetry() >= 3) {
            return ResultVo.failure("重试次数已用完");
        }
        var wrapper = this.recipientService.getRecipientListByGroupId(emailRecord.getGroupId());
        List<Recipient> recipientList = this.recipientService.list(wrapper);
        // 获取状态为0的邮件日志记录的收件人ID
        List<Long> failedRecipientIds = emailLogService.list(
                        Wrappers.<EmailLog>lambdaQuery()
                                .in(EmailLog::getRecipientId, recipientList.stream().map(Recipient::getId).collect(Collectors.toList()))
                )
                .stream()
                // 将EmailLog对象映射为包含收件人ID和最新时间的Map
                .collect(Collectors.toMap(
                        EmailLog::getRecipientId, // 作为键
                        emailLog -> emailLog, // 作为值
                        (existing, replacement) -> existing.getSendTime().isAfter(replacement.getSendTime()) ? existing : replacement // 如果有重复的ID，保留时间最新的记录
                ))
                .entrySet() // 获取所有条目，即EmailLog对象
                .stream() // 转换为流
                .filter(entry -> entry.getValue().getStatus() == 0) // 过滤状态为0的记录
                .map(Map.Entry::getKey) // 映射为收件人ID
                .toList(); // 转换为List
        // 过滤recipientList，只保留存在于failedRecipientIds中的记录
        List<Recipient> failedRecipients = recipientList.stream()
                .filter(recipient -> failedRecipientIds.contains(recipient.getId()))
                .toList();
        LocalDateTime sendDateTime = null;
        if (StrUtil.isNotEmpty(sendTime)) {
            try {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                sendDateTime = LocalDateTime.parse(sendTime, formatter);
            } catch (DateTimeParseException e) {
                return ResultVo.failure(ResultCode.ERROR);
            }
        }
        boolean updatedById = this.update(Wrappers.<EmailRecord>lambdaUpdate()
                .set(EmailRecord::getStatus, 2)
                .set(EmailRecord::getSendTime, sendDateTime)
                .set(EmailRecord::getRetry, emailRecord.getRetry() + 1)
                .eq(EmailRecord::getId, emailRecord.getId()));
        if (updatedById) {
            this.sendEmailService.sendEmail(emailRecord, failedRecipients);
            return ResultVo.success(emailRecord);
        }
        return ResultVo.failure(ResultCode.ERROR);
    }

    @Override
    public ResultVo<?> getEmailById(Long id) {
        EmailRecord emailRecord = this.getById(id);
        return ResultVo.success(emailRecord);
    }
}

